#!/usr/bin/python

# This software is distributed under the Simplified BSD License:
# Copyright (c) 2008, Chris Venter <chris.venter@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, 
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, 
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice, 
# this list of conditions and the following disclaimer in the documentation 
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
# SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
# DAMAGE.

# testing

# this is a quick and dirty implementation of a python script which
# sucks in a X-test.xml description and outputs the c++ code which will
# perform the test code described within.
# It currently uses python 2.5.2, so may not work on Python 3.0

# it is part of the generik/testing.h unit-testing framework.
# Please see that file for more details.
# An example xml file can be found in "testing.xml"

# it is planned to write tests for all the generik functionality
# in this format, and store them in the tests directory

from xml.dom import minidom

import sys
import getopt

def usage():
	print "Usage goes here..."

try:                                
	opts, args = getopt.getopt(sys.argv, "h", ["help"])
except getopt.GetoptError:
	usage()
	sys.exit(2)
if opts in ("-h", "--help"):
	usage()
	sys.exit()

source = "".join(args[1:])

testgroupname = ''

# parse the xml file
xmldoc = minidom.parse(source)

def addtext(node):
	text = '';
	for i in node.childNodes:
		# it is text, so simply dump it to the output
		newtext = '';
		if (i.nodeType == minidom.Node.TEXT_NODE):
			newtext = i.data.strip();
		if (i.nodeType == minidom.Node.CDATA_SECTION_NODE):
			newtext = i.data.strip();
		if len(newtext) > 0:
			text = text + newtext;
	# we need to do some post-processing: split text into strings seperated by newlines,
	# and add a \t to each string. This is a crude attempt at pretty printing.
	text = '\t' + '\n\t'.join(text.split('\n'));
	return text;

testers = {}

def addtester(node):
	testername = node.getAttribute('name');
	setup = '';
	teardown = '';
	tests = {};
	# go through all the children, loooking for setup, teardown and test code.
	for i in node.childNodes:
		# if it is an element?
		if (i.nodeType == minidom.Node.ELEMENT_NODE):
			# setup code?
			if (i.tagName == 'setup'):
				setup = setup + addtext(i);
				pass
			# teardown code?
			if (i.tagName == 'teardown'):
				teardown = teardown + addtext(i);
				pass
			# test code?
			if (i.tagName == 'test'):
				testname = i.getAttribute('name');
				tests[testname] = addtext(i);
				pass
	testnames = [];

	print
	print '///////////'
	print '// tests //'
	print '///////////'
	print
	
	for testname, testcode in tests.iteritems():
		print '// ---', testname, '---'
		print 'void %s()' % (testname)
		print '{'
		print '\tGENERIK_TRACER;'
		print
		#print 'try {'
		print '\t// ------------------'
		print '\t// ---   setup    ---'
		print '\t// ------------------'
		print setup;
		print 
		print '\t// ------------------'
		print '\t// ---  testcode  ---'
		print '\t// ------------------'
		print testcode;
		print
		print '\t// ------------------'
		print '\t// ---  teardown  ---'
		print '\t// ------------------'
		print teardown;
		print
		#print '} catch(...) { throw; } // default exception handler'
		print '}'
		print
		testnames.append(testname);
	
	testers[testername] = testnames;


def addpre(node):
	print '/////////'
	print '// pre //'
	print '/////////'
	print 
	for i in node.childNodes:
		# it is text, so simply dump it to the output
		if (i.nodeType == minidom.Node.TEXT_NODE):
			print(i.data);
		if (i.nodeType == minidom.Node.CDATA_SECTION_NODE):
			print(i.data);
	print 

def addtestgroup(node):
	global testgroupname;
	# the root element should be the testgroup. Get its name attribute!:
	testgroupname = node.getAttribute('name');
	
	print '/* testgroupname: ', testgroupname;
	print 'This code has been auto-generated from:', source
	print 'using the generik::testing unit-testing facility'
	print '*/'
	print '#ifndef GENERIK_TRACING'
	print '\t#define GENERIK_TRACING'
	print '#endif'
	print '#include <generik/tracing.h>'
	print 'GENERIK_TRACER_GLOBAL'
	print
	print '#include <generik/testing.h>'

	print
	print
	
	for i in node.childNodes:
		# if it is a tester element, create a new tester instance
		if (i.nodeType == minidom.Node.ELEMENT_NODE):
			if (i.tagName == 'tester'):
				addtester(i);
			if (i.tagName == 'pre'):
				addpre(i);

# for each child of in the document, dispatch on its type and tagname
# we ignore comments, and only handle one testgroup
for i in xmldoc.childNodes:
	if (i.nodeType == minidom.Node.ELEMENT_NODE):
		if (i.tagName == 'testgroup'):
			addtestgroup(i);
			break;

print
print
print

print '/////////////'
print '// testers //'
print '/////////////'
print

for tester, tests in testers.iteritems():
	print '// ---', tester, '---'
	print 'struct %s' % (tester)
	print ': generik::tester' # this sh/could be extraced too
	print '{'
	print '\t%s()' % tester
	print '\t{'
	for i in tests:
		print '\t\tGENERIK_ADDTEST(%s);' % i
	print '\t}'
	print
	
	print '\tbool check(testfunction function)'
	print '\t{'
	print '\t\tGENERIK_TRACER;'
	print '\t\ttry'
	print '\t\t{'
	print '\t\t\treturn generik::tester::check(function);'
	print '\t\t}'
	print '\t\t//\\todo pushed exceptions go here...'
	print '\t\t//catch (TYPE& t)'
	print '\t\t//{'
	print '\t\t//\t// report this exception in a human readable way'
	print '\t\t//}'
	print '\t\tcatch(...) { throw; } // rethrow block'
	#print '\t\tstd::cerr<<"# increasing error count"<<std::endl;'
	print '\t\t++_errors;'
	print '\t\treturn false;'
	print '\t}'
	
	print '};'

print
print
print

print '/////////////////'
print '//  testgroup  //'
print '/////////////////'
print

print 'struct %s' % (testgroupname)
print ': generik::testgroup' # this sh/could be extraced too
print '{'
print '\t%s(const std::string& name, size_t seconds_in)' % (testgroupname)
print '\t: generik::testgroup(name, seconds_in)'
print '\t{'
for tester in testers.keys():
	print '\t\tGENERIK_ADDTESTER(%s)' % (tester)
print '\t}'
print '};'

print

print 'int main(int argc, char** argv)'
print'{'
print'\tGENERIK_TRACER;'
print
print '\tgenerik::testoptions _options(argc, argv);'
print
print '\tGENERIK_TRACE_VALUE(_options._timeout);'
print '\tGENERIK_TRACE_VALUE(_options._logfilename);'
print
print '\tstd::ostream* _stream = generik::tracing::set_stream(_options._logfilename);'
print 
print '\t%s t("%s", _options._timeout);' % (testgroupname, testgroupname)
print '\tint errors = t.exec();'
print '\tGENERIK_TRACE((errors == 0 ? "OK" : "FAIL"));'
print
print '\tif (_stream)'
print '\t{'
print '\t\t_stream = generik::tracing::set_stream(_stream);'
print '\t\tdelete _stream;'
print '\t}'
print
print '\treturn errors;'
print '}'
print





